package com.octopus.optiworks.action;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jbpm.api.activity.ActivityExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.octopus.optiworks.model.Product;
import com.octopus.optiworks.model.Step140_1Data;
import com.octopus.optiworks.model.Step170_1Data;
import com.octopus.optiworks.model.StepSpec;
import com.octopus.optiworks.service.StepService;

@Service
public class StepActionUtil {
	
	private static Log log = LogFactory.getLog(StepActionUtil.class);
	
	public static final String STEP_00		= "step00";
	public static final String STEP_10_1	= "step10_1";
	public static final String STEP_10_2	= "step10_2";
	public static final String STEP_20		= "step20";
	public static final String STEP_30_1	= "step30_1";
	public static final String STEP_30_2	= "step30_2";
	public static final String STEP_40		= "step40";
	public static final String STEP_50		= "step50";
	public static final String STEP_60		= "step60";
	public static final String STEP_70		= "step70";
	public static final String STEP_80		= "step80";
	public static final String STEP_90		= "step90";
	public static final String STEP_100_1	= "step100_1";
	public static final String STEP_100_2	= "step100_2";
	public static final String STEP_100_3	= "step100_3";
	public static final String STEP_110		= "step110";
	public static final String STEP_120_1	= "step120_1";
	public static final String STEP_120_2	= "step120_2";
	public static final String STEP_130		= "step130";
	public static final String STEP_140_1	= "step140_1";
	public static final String STEP_140_2	= "step140_2";
	public static final String STEP_150		= "step150";
	public static final String STEP_160		= "step160";
	public static final String STEP_170_1	= "step170_1";
	public static final String STEP_170_2	= "step170_2";
	public static final String STEP_180		= "step180";
	
	public static final String STEP_ALL		= "all";
	public static final String STEP_COMPLETE= "Complete";
	
	public static final String TESTRESULT			= "testResult";
	public static final String TESTRESULT_PASS 		= "Pass";
	public static final String TESTRESULT_FAILURE 	= "Fail";
	public static final String TESTRESULT_WAIVE 	= "Waive";
	public static final String TESTRESULT_SPEC		= "Spec";
	
	public static final String STEP_GO			= "Go";
	public static final String STEP_GO_FORWARD	= "Forward";
	public static final String STEP_GO_BACK		= "Back";
	
	@Autowired
	private StepService stepService;
	
	public static String toWaiveFail(String from, String to){
		return to + " [" + from + "]";
	}
	
	public static String unWaiveFail(String from){
		return from.substring(from.indexOf("[") + 1, from.indexOf("]"));
	}
	
	public Map<String, ?> getSpecAutoResult(ActivityExecution execution, Map<String, String> map){
		Long specId = (Long)execution.getProcessInstance().getVariable("spec");
		List<String> listFailedCells = new ArrayList<String>();
		String processId = execution.getProcessInstance().getId();
		String step = execution.getActivityName();
		map.put(STEP_GO, STEP_GO_FORWARD);
		
		if(!map.get(TESTRESULT).equals(TESTRESULT_SPEC))
			return map;
		log.debug("Spec auto judgement. Step: " + step + "; SpecID: " + specId + "; TestResult: " + map.get(TESTRESULT));
		map.put(TESTRESULT, TESTRESULT_PASS);
		StepSpec spec = this.stepService.getStepSpec(specId);
		if(spec == null)
			return map;
		
		boolean match = true;
		Class<?> failedStep = null;
		Product product = this.stepService.getProductByProcess(processId);
		product = this.stepService.getProduct(product.getId());
		List<Object> dataSteps = this.getStepData(product, step);
		List<Object> dataSpecs = this.getSpecData(spec, step);
		List<Object> operationSpecs = this.getSpecOperatioin(spec, step);
		for(int i = 0; i < operationSpecs.size(); i++){
			Object operationSpec = operationSpecs.get(i);
			Object dataStep = dataSteps.get(i);
			Object dataSpec = dataSpecs.get(i);
			Field[] fields = operationSpec.getClass().getDeclaredFields();
			if(fields != null && fields.length > 0){
				for(Field field : fields){
					if(!field.getType().getName().equals("java.lang.String"))
						continue;
					Object op = this.getFieldValue(field, operationSpec);
					if(op == null || op.equals(StepSpec.OPERATION_NUM_NA) || op.equals(StepSpec.OPERATION_STR_NA))
						continue;
					Object valueStep = this.getFieldValue(field, dataStep);
					Object valueSpec = this.getFieldValue(field, dataSpec);
					if(valueSpec == null || valueSpec.toString().trim().length() == 0)
						continue;
					if(valueStep == null || valueStep.toString().trim().length() == 0){
						match = false;
						failedStep = null==failedStep?dataStep.getClass():failedStep;
						listFailedCells.add(field.getName());
						continue;
					}
					boolean m = true;
					if(op.equals(StepSpec.OPERATION_NUM_EQ)){
						m = Double.compare(Double.parseDouble(valueStep.toString()), Double.parseDouble(valueSpec.toString())) == 0;
					}else if(op.equals(StepSpec.OPERATION_NUM_LT)){
						m = Double.compare(Double.parseDouble(valueStep.toString()), Double.parseDouble(valueSpec.toString())) < 0;
					}else if(op.equals(StepSpec.OPERATION_NUM_MT)){
						m = Double.compare(Double.parseDouble(valueStep.toString()), Double.parseDouble(valueSpec.toString())) > 0;
					}else if(op.equals(StepSpec.OPERATION_NUM_NOEQ)){
						m = Double.compare(Double.parseDouble(valueStep.toString()), Double.parseDouble(valueSpec.toString())) != 0;
					}else if(op.equals(StepSpec.OPERATION_NUM_NOLT)){
						m = !(Double.compare(Double.parseDouble(valueStep.toString()), Double.parseDouble(valueSpec.toString())) < 0);
					}else if(op.equals(StepSpec.OPERATION_NUM_NOMT)){
						m = !(Double.compare(Double.parseDouble(valueStep.toString()), Double.parseDouble(valueSpec.toString())) > 0);
					}else if(op.equals(StepSpec.OPERATION_STR_EQ)){
						m = valueStep.toString().equals(valueSpec.toString());
					}else if(op.equals(StepSpec.OPERATION_STR_NOEQ)){
						m = !valueStep.toString().equals(valueSpec.toString());
					}
					match = match & m;
					if(!m){
						failedStep = null==failedStep?dataStep.getClass():failedStep;
						listFailedCells.add(field.getName());
						continue;
					}
				}
			}
			if(!match){
				failedStep = null==failedStep?dataStep.getClass():failedStep;
				continue;
			}
		}
		if(!match)
			map.put(TESTRESULT, TESTRESULT_FAILURE);
		
		if(failedStep != null && (failedStep.equals(Step140_1Data.class) || failedStep.equals(Step170_1Data.class)))
			map.put(STEP_GO, STEP_GO_BACK);
		else
			map.put(STEP_GO, STEP_GO_FORWARD);
		
		String[] failedCells = new String[listFailedCells.size()];
		execution.getProcessInstance().setVariable("failedCells", listFailedCells.toArray(failedCells));
		
		return map;
	}
	
	private Object getFieldValue(Field field, Object obj){
        String fieldName=field.getName();   
        String stringLetter=fieldName.substring(0, 1).toUpperCase();   
        String getName="get"+stringLetter+fieldName.substring(1);   
        //String setName="set"+stringLetter+fieldName.substring(1);   
		try {
			Method getMethod = obj.getClass().getMethod(getName, new Class[]{});
            //Method setMethod = obj.getClass().getMethod(setName, new Class[]{field.getType()});   
            Object value=getMethod.invoke(obj, new Object[]{}); 
            return value;
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private List<Object> getSpecOperatioin(StepSpec spec, String step){
		List<Object> result = new ArrayList<Object>();
		if(step.equals(STEP_10_1)){
			result.add(spec.getStep10_1Operation());
		}else if(step.equals(STEP_10_2)){
			result.add(spec.getStep10_2Operation());	
		}else if(step.equals(STEP_30_2)){
			result.add(spec.getStep30_2Operation());
		}else if(step.equals(STEP_80)){
			result.add(spec.getStep80Operation());
		}else if(step.equals(STEP_140_2)){
			result.add(spec.getStep140_1Operation());
			result.add(spec.getStep140_2Operation());
		}else if(step.equals(STEP_160)){
			result.add(spec.getStep160Operation());
		}else if(step.equals(STEP_170_2)){
			result.add(spec.getStep170_1Operation());
			result.add(spec.getStep170_2Operation());
		}
		return result;
	}
	
	private List<Object> getSpecData(StepSpec spec, String step){
		List<Object> result = new ArrayList<Object>();
		if(step.equals(STEP_10_1)){
			result.add(spec.getStep10_1Data());
		}else if(step.equals(STEP_10_2)){
			result.add(spec.getStep10_2Data());
		}else if(step.equals(STEP_30_2)){
			result.add(spec.getStep30_2Data());
		}else if(step.equals(STEP_80)){
			result.add(spec.getStep80Data());
		}else if(step.equals(STEP_140_2)){
			result.add(spec.getStep140_1Data());
			result.add(spec.getStep140_2Data());
		}else if(step.equals(STEP_160)){
			result.add(spec.getStep160Data());
		}else if(step.equals(STEP_170_2)){
			result.add(spec.getStep170_1Data());
			result.add(spec.getStep170_2Data());
		}
		return result;
	}
	
	private List<Object> getStepData(Product product, String step){
		List<Object> result = new ArrayList<Object>();
		if(step.equals(STEP_10_1)){
			result.add(product.getStep10_1Datas().toArray()[0]);
		}else if(step.equals(STEP_10_2)){
			result.add(product.getStep10_2Datas().toArray()[0]);
		}else if(step.equals(STEP_30_2)){
			result.add(product.getStep30_2Datas().toArray()[0]);
		}else if(step.equals(STEP_80)){
			result.add(product.getStep80Datas().toArray()[0]);
		}else if(step.equals(STEP_140_2)){
			result.add(product.getStep140_1Datas().toArray()[0]);
			result.add(product.getStep140_2Datas().toArray()[0]);
		}else if(step.equals(STEP_160)){
			result.add(product.getStep160Datas().toArray()[0]);
		}else if(step.equals(STEP_170_2)){
			result.add(product.getStep170_1Datas().toArray()[0]);
			result.add(product.getStep170_2Datas().toArray()[0]);
		}
		return result;
	}

}
